package atlantik.ui;

import java.awt.BorderLayout;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import modelz.UpdateListener;
import modelz.UpdateableCollectionListener;
import atlantik.game.Atlantik;
import atlantik.game.Game;
import atlantik.game.action.AtlantikClientAction;
import atlantik.game.action.JoinGameAction;

public class GameList extends JPanel {

	private final GameListModel model = new GameListModel();

	private final JTable table = new JTable(model);

	private final JButton join;

	private final JoinGameAction joinAction;

	private final UpdateListener<Game> updateListener = new UpdateListener<Game>() {
		public void updated(Game item) {
			model.update(item);
		}
	};

	private final UpdateableCollectionListener<Game> collectionListener = new UpdateableCollectionListener<Game>() {
		public void cleared() {
			model.clear();
		}

		public void added(Game item) {
			model.add(item);
			item.addListener(updateListener);
		}

		public void removed(Game item) {
			item.removeListener(updateListener);
			model.remove(item);
		}
	};

	GameList(Atlantik client) {
		super();
		client.getState().games().addListener(collectionListener);
		joinAction = new JoinGameAction(client);
		join = new JButton(new ClientAction<AtlantikClientAction>(joinAction));
		setLayout(new BorderLayout());
		add(table);
		add(join, BorderLayout.SOUTH);
		table.getSelectionModel().addListSelectionListener(
				new ListSelectionListener() {
					public void valueChanged(ListSelectionEvent e) {
						int index = e.getFirstIndex();
						if (index == -1)
							joinAction.setSelectedGame(null);
						else
							joinAction.setSelectedGame(model.getGameAt(index));
					}
				});
	}

	private class GameListModel implements TableModel {

		private static final int NAME = 0;

		private static final int DESCRIPTION = 1;

		private static final int ID = 2;

		private static final int PLAYERS = 3;

		private static final int COLUMNS = 4;

		private final List<TableModelListener> listeners = new CopyOnWriteArrayList<TableModelListener>();

		private final List<Game> games = new CopyOnWriteArrayList<Game>();

		public void addTableModelListener(TableModelListener l) {
			listeners.add(l);
		}

		public void remove(Game item) {
			int row = games.indexOf(item);
			games.remove(item);

			TableModelEvent e = new TableModelEvent(this, row, row,
					TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE);
			for (TableModelListener l : listeners)
				l.tableChanged(e);
		}

		public void add(Game item) {
			if (!games.add(item))
				return; // TODO WTF should we do here?

			int row = games.indexOf(item);
			TableModelEvent e = new TableModelEvent(this, row, row,
					TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT);
			for (TableModelListener l : listeners)
				l.tableChanged(e);
		}

		public void clear() {
			int lastRow = games.size() - 1;
			if (lastRow == -1)
				return; // empty already

			TableModelEvent e = new TableModelEvent(this, 0, lastRow,
					TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE);
			for (TableModelListener l : listeners)
				l.tableChanged(e);
			games.clear();
		}

		public void update(Game item) {
			int row = games.indexOf(item);
			TableModelEvent e = new TableModelEvent(this, row);
			for (TableModelListener l : listeners)
				l.tableChanged(e);
		}

		public int getColumnCount() {
			return COLUMNS;
		}

		public Class< ? > getColumnClass(int columnIndex) {
			switch (columnIndex) {
			case ID:
			case PLAYERS:
				return Integer.class;
			case NAME:
			case DESCRIPTION:
				return String.class;
			default:
				throw new IndexOutOfBoundsException();
			}
		}

		public String getColumnName(int columnIndex) {
			switch (columnIndex) {
			case ID:
				return "Id";
			case NAME:
				return "Game";
			case DESCRIPTION:
				return "Description";
			case PLAYERS:
				return "Players";
			default:
				throw new IndexOutOfBoundsException();
			}
		}

		private Object getValue(Game game, int columnIndex) {
			switch (columnIndex) {
			case ID:
				return game.getId();
			case NAME:
				return game.getName();
			case DESCRIPTION:
				return game.getDescription();
			case PLAYERS:
				return game.getPlayers();
			default:
				throw new IndexOutOfBoundsException();
			}
		}

		public int getRowCount() {
			return games.size();
		}

		public Object getValueAt(int rowIndex, int columnIndex) {
			return getValue(games.get(rowIndex), columnIndex);
		}

		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return false;
		}

		public void removeTableModelListener(TableModelListener l) {
			listeners.remove(l);
		}

		public void setValueAt(Object value, int rowIndex, int columnIndex) {
			throw new UnsupportedOperationException();
		}

		public Game getGameAt(int index) {
			if( games.size() <= index )
				return null;
			return games.get(index);
		}

	}
}
